package bb.lanxing.calc.processer;

import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;

import bb.lanxing.App;
import bb.lanxing.R;
import bb.lanxing.calc.data.DisplayPoint;
import bb.lanxing.calc.data.GpsPoint;
import bb.lanxing.calc.data.SourcePoint;
import bb.lanxing.calc.gps.GPSSearchHeartbeatStrategy;
import bb.lanxing.calc.gps.GPSSearchStrategy;
import bb.lanxing.calc.notify.SpeedAlert;
import bb.lanxing.manager.RemoteSharedPreference;
import bb.lanxing.manager.SPConstant;
import bb.lanxing.manager.SharedManager;
import bb.lanxing.manager.SystemLocationManager;
import bb.lanxing.manager.SystemSensorManager;
import bb.lanxing.model.WorkoutDatabaseHelper;
import bb.lanxing.model.database.Trackpoint;
import bb.lanxing.model.database.Workout;
import bb.lanxing.mvp.BroadcastCons;
import bb.lanxing.util.Log;
import bb.lanxing.util.TtsSettingHelper;

import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.List;

public class RecordWorkoutProcessor extends BaseWorkoutProcessor {
    final static private String TAG = "RecordWorkoutProcessor";
    private static final int AVOID_POINT_COUNT_MIN = 3;
    private static final long GPS_WARNING_INTERVAL = 60000L;
    private static final int MIN_STORE_NUM = 10;
    private static final int MSG_FLUSH_LOCATION = 3;
    private static final int MSG_GPS_GONE = 1;
    private static final int MSG_STORE_LOCATION = 2;
    private static final long SAVE_LOCATION_INTERVAL = 60000L;
    private BgHandler bgHandler;
    private Config config;
    private GPSSearchStrategy gpsSearchStrategy;
    private HandlerThread handlerThread;
    private int avoidPointCount = 0;
    private long lastStoreTime = 0;
    private boolean gpsReceived = false;

    private void initSoundManager() {
    }

    private class Config {
        int altitudeAdjustValue;
        int altitudeSource;
        boolean ttsEnable;
        boolean ttsGPSEnable;
        int ttsIntervalDistance;
        int ttsIntervalDuration;

        private Config() {
        }
    }

    @Override
    public void initCalc() {
        super.initCalc();
        if (RemoteSharedPreference.getInstance().getBoolean(SPConstant.KEY_TTS_SPEED_WARNING_ENABLE, false)) {
            initSpeedAlert();
        }
    }

    private void initSpeedAlert() {
        speedAlert = new SpeedAlert(App.getContext(), R.raw.beep);
        speedAlert.setAlertEnable(true);
        speedAlert.setAlertValue(RemoteSharedPreference.getInstance().getSpeedWarningValue());
    }

    @Override
    public void initConfigs() {
        config = new Config();
        config.ttsEnable = RemoteSharedPreference.getInstance().getTTSSoundEnable();
        config.ttsGPSEnable = RemoteSharedPreference.getInstance().getGPSSoundEnable();
        config.ttsIntervalDistance = RemoteSharedPreference.getInstance().getTTSDistance();
        config.ttsIntervalDuration = RemoteSharedPreference.getInstance().getTTSDuration();
        ttsChecker.updateConfigs(config.ttsEnable, config.ttsGPSEnable, config.ttsIntervalDistance, config.ttsIntervalDuration);
        config.altitudeSource = RemoteSharedPreference.getInstance().getAltitudeSource();
        config.altitudeAdjustValue = RemoteSharedPreference.getInstance().getAltitudeAdjustValue();
        SystemLocationManager.getInstance().updateAltitudeConfigs(config.altitudeSource, config.altitudeAdjustValue);
    }

    @Override
    public void updateConfigs(String key, Object value) {
        if (SPConstant.KEY_TTS_SOUND_ENABLE.equals(key)) {
            config.ttsEnable = (Boolean) value;
            ttsChecker.updateConfigs(config.ttsEnable, config.ttsGPSEnable, config.ttsIntervalDistance, config.ttsIntervalDuration);
        } else if (SPConstant.KEY_GPS_SOUND_ENABLED.equals(key)) {
            config.ttsGPSEnable = (Boolean) value;
            ttsChecker.updateConfigs(config.ttsEnable, config.ttsGPSEnable, config.ttsIntervalDistance, config.ttsIntervalDuration);
        } else if (SPConstant.KEY_TTS_DISTANCE.equals(key)) {
            config.ttsIntervalDistance = TtsSettingHelper.parseTTSDistance((String) value);
            ttsChecker.updateConfigs(config.ttsEnable, config.ttsGPSEnable, config.ttsIntervalDistance, config.ttsIntervalDuration);
        } else if (SPConstant.KEY_TTS_DURATION.equals(key)) {
            config.ttsIntervalDuration = TtsSettingHelper.parseTTSDuration((String) value);
            ttsChecker.updateConfigs(config.ttsEnable, config.ttsGPSEnable, config.ttsIntervalDistance, config.ttsIntervalDuration);
        } else if (SPConstant.KEY_ALTITUDE_SOURCE_V2.equals(key)) {
            config.altitudeSource = Integer.parseInt(value.toString());
//            boolean isPressureValid = SystemSensorManager.getInstance().hasPressureSensor(App.getContext());
            if (config.altitudeSource == 1/* || isPressureValid*/) {
                SystemSensorManager.getInstance().startPressureSensor();
            } else {
                SystemSensorManager.getInstance().stopPressureSensor();
            }
            SystemLocationManager.getInstance().updateAltitudeConfigs(config.altitudeSource, config.altitudeAdjustValue);
        } else if (SPConstant.KEY_ALTITUDE_ADJUST_VALUE_V2.equals(key)) {
            config.altitudeAdjustValue = Integer.parseInt(value.toString());
            SystemLocationManager.getInstance().updateAltitudeConfigs(config.altitudeSource, config.altitudeAdjustValue);
        } else if (SPConstant.KEY_TTS_SPEED_WARNING_ENABLE.equals(key)) {
            if ((Boolean) value) {
                if (speedAlert == null) {
                    initSpeedAlert();
                }
            } else if (speedAlert != null) {
                speedAlert.release();
                speedAlert = null;
            }
        } else if (SPConstant.KEY_TTS_SPEED_WARNING_VALUE.equals(key) && speedAlert != null) {
            speedAlert.setAlertValue(Integer.parseInt(value.toString()));
        }
    }

    @Override
    public void init(int[] dataTypes, Workout lastWorkout) {
        super.init(dataTypes, lastWorkout);
//        boolean isPressureValid = SystemSensorManager.getInstance().hasPressureSensor(App.getContext());
        SystemSensorManager.getInstance().init(App.getContext());
        if (config.altitudeSource == 1/* || isPressureValid*/) {
            SystemSensorManager.getInstance().startPressureSensor();
        }
        initSoundManager();
        handlerThread = new HandlerThread("location-list-store");
        handlerThread.start();
        bgHandler = new BgHandler(handlerThread.getLooper(), this);
        if (!isTrainingMode() 
            &&!isIndoorBikeMode()
            &&!isBikeTrainerMode()
            &&!isRowerMode()
            &&!isCrossTrainerMode()) {
                gpsSearchStrategy = new GPSSearchHeartbeatStrategy();
                gpsSearchStrategy.onGpsSwitch(true);
        }
    }

    @Override
    public void release() {
        GPSSearchStrategy gPSSearchStrategy;
        super.release();
        if (!isTrainingMode() && (gPSSearchStrategy = gpsSearchStrategy) != null) {
            if(!isIndoorBikeMode() 
               && !isCrossTrainerMode()
               && !isRowerMode()
               && !isBikeTrainerMode()){
                gPSSearchStrategy.onGpsSwitch(false);
            }
        }
        if (speedAlert != null) {
            speedAlert.release();
            speedAlert = null;
        }
        SystemSensorManager.getInstance().stopPressureSensor();
        SystemSensorManager.terminate();
        releaseSoundManager();
        if (handlerThread != null) {
            handlerThread.quit();
            handlerThread = null;
        }
        if (bgHandler != null) {
            bgHandler.removeCallbacksAndMessages(null);
            bgHandler = null;
        }
        avoidPointCount = 0;
    }

    @Override
    public boolean checkPoint(SourcePoint sourcePoint) {
        if (sourcePoint == null) {
            return false;
        }
        if (filterEnable) {
            if (isTrainingMode()
                ||isIndoorBikeMode()
                ||isCrossTrainerMode()
                ||isBikeTrainerMode()
                ||isRowerMode()
                ) {
                sourcePointPair.update(sourcePoint);
                if (isPaused(sourcePoint)) {
                    workout.setWorkStatus(Workout.STATUS_PAUSED);//18
                    return false;
                }
                workout.setWorkStatus(Workout.STATUS_WORKING);//17
                return true;
            } else if (isGpsLost(sourcePoint)) {
                workout.setWorkStatus(Workout.STATUS_GPS_SEARCHING);//16
               Log.logd(TAG, "checkPoint isGpsLost...");
                return false;
            } else if (isSamePoints(sourcePoint)) {
               Log.logd(TAG, "checkPoint isSamePoints...");
                return false;
            } else {
               Log.logd(TAG, "checkPoint Workout.STATUS_WORKING...");
                workout.setWorkStatus(Workout.STATUS_WORKING);//17
                if (!checkPointValid(sourcePoint)) {
                   Log.logd(TAG, "checkPoint checkPointValid NOT valid...");
                    return false;
                }
            }
        }
        sourcePointPair.update(sourcePoint);
       Log.logd(TAG, "checkPoint sourcePointPair.update...pauseSportState=" + userInitiatedPause);
        if (isPaused(sourcePoint) || userInitiatedPause) {
           Log.logd(TAG, "checkPoint isPaused...");
            workout.setWorkStatus(Workout.STATUS_PAUSED);
            return false;
        }
        return true;
    }

    @Override
    public DisplayPoint process(SourcePoint sourcePoint) {
       Log.logd(TAG, "DisplayPoint process ...workout=" + workout);
        if (workout == null) {
            return displayPoint;
        }
        if (checkPoint(sourcePoint)) {
           Log.logd(TAG, "DisplayPoint process ...after checkPoint()");
            calcPoint(sourcePoint);
            calcWorkout(sourcePoint);
            storePoint(sourcePoint);
        }
        updateDisplayPoint(sourcePoint);
        if (!isTrainingMode()
            && !isIndoorBikeMode()
            && !isBikeTrainerMode()
            && !isRowerMode()
            && !isCrossTrainerMode()) {
            Log.logd(TAG, "DisplayPoint process ...displayPoint.getSportState()=" + displayPoint.getSportState());
            if (displayPoint.getSportState() == Workout.STATUS_GPS_SEARCHING) {//16
                warnGpsGoneIfNeed();
                if (gpsSearchStrategy != null) {
                    gpsSearchStrategy.onGpsStatus(false);
                }
            } else if (displayPoint.getSportState() == Workout.STATUS_WORKING || displayPoint.getSportState() == Workout.STATUS_PAUSED) {//17, 18
                warnGpsReceiveIfNeed();
                if (gpsSearchStrategy != null) {
                    gpsSearchStrategy.onGpsStatus(true);
                }
            }
        }
        return displayPoint;
    }

    private void releaseSoundManager() {
        if (bgHandler != null) {
            bgHandler.removeMessages(MSG_GPS_GONE);//1
        }
    }

    public void warnGpsGoneIfNeed() {
       Log.logd(TAG, "warnGpsGoneIfNeed... gpsReceived=" + gpsReceived);
        if (gpsReceived) {
            gpsReceived = false;
            ttsChecker.notifyGPSLost();
            if (bgHandler != null) {
               Log.logd(TAG, "warnGpsGoneIfNeed... MSG_GPS_GONE");
                bgHandler.removeMessages(MSG_GPS_GONE);
                bgHandler.sendEmptyMessageDelayed(MSG_GPS_GONE, GPS_WARNING_INTERVAL);
            }
           Log.logd(TAG, "warnGpsGoneIfNeed... BroadcastCons.ACTION_SPORT_GPS_STATUE");
            App.getContext().sendBroadcast(new Intent(BroadcastCons.ACTION_SPORT_GPS_STATUE)
                    .putExtra(BroadcastCons.ACTION_EXTRA_GPS_STATE, false), BroadcastCons.PERMISSION_SPORT_STATUES);
        }
    }

    private void warnGpsReceiveIfNeed() {
        if (gpsReceived) {
            return;
        }
        gpsReceived = true;
        ttsChecker.notifyGPSReceived();
        if (bgHandler != null) {
            bgHandler.removeMessages(MSG_GPS_GONE);//1
        }
        App.getContext().sendBroadcast(new Intent(BroadcastCons.ACTION_SPORT_GPS_STATUE)
                .putExtra(BroadcastCons.ACTION_EXTRA_GPS_STATE, true), BroadcastCons.PERMISSION_SPORT_STATUES);
    }

    @Override
    public void storePoint(SourcePoint sourcePoint) {
       Log.logd(TAG, "storePoint <<<<");
        if (avoidBeginningPoints()) {
            return;
        }
        if (intervalCalc.isNeedStore(sourcePoint)) {
            Trackpoint makeTrackpoint = makeTrackpoint(workout, sourcePoint);
            Log.logd(TAG, "storePoint, workout.getDistance() = " + workout.getDistance());
            if (workout.getDistance() < 1000.0d && workout.getSport() != Workout.TYPE_TRAINING) {
                long a = WorkoutDatabaseHelper.save(makeTrackpoint);
                WorkoutDatabaseHelper.save(workout);
            } else {
                if (bgHandler != null) {
                    bgHandler.sendMessage(bgHandler.obtainMessage(MSG_STORE_LOCATION, makeTrackpoint));//2
                }
            }
        }
        saveLocation2SPIfNeed(sourcePoint);
        uploadLocationIfNeed(sourcePoint);
        Log.logd(TAG, "storePoint >>>>");
    }

    @Override
    public void flushPoint(boolean sync) {
        if (bgHandler != null) {
            if (!sync) {
                bgHandler.sendEmptyMessage(MSG_FLUSH_LOCATION);//3
            } else {
                bgHandler.flushLocation();
            }
        }
    }

    private boolean avoidBeginningPoints() {
        int i;
        if (!filterEnable || (i = avoidPointCount) >= AVOID_POINT_COUNT_MIN) {
            return false;
        }
        avoidPointCount = i + 1;
        return true;
    }

    private void uploadLocationIfNeed(SourcePoint sourcePoint) {
        if (sourcePoint == null || !sourcePoint.hasGps()) {
            return;
        }

        App.getContext().uploadMyLocationIfNeed(sourcePoint.getLatLng(),
                displayPoint, workout, 0, false, null);
    }

    private void saveLocation2SPIfNeed(SourcePoint sourcePoint) {
        long currentTimeMillis = System.currentTimeMillis();
        if (currentTimeMillis - lastStoreTime > SAVE_LOCATION_INTERVAL) {
            lastStoreTime = currentTimeMillis;
            GpsPoint gpsPoint = sourcePoint.getGpsPoint();
            if (gpsPoint != null) {
                SharedManager.getInstance().setCurLocationInfoWithMP(gpsPoint.getLatitude(), gpsPoint.getLongitude(), null);
            }
        }
    }

    public static class BgHandler extends Handler {
        private final List<Trackpoint> locationList;
        private WeakReference<RecordWorkoutProcessor> reference;

        private BgHandler(Looper looper, RecordWorkoutProcessor recordWorkoutProcessor) {
            super(looper);
            locationList = new LinkedList();
            reference = new WeakReference<>(recordWorkoutProcessor);
        }

        private void storeLocation(Trackpoint trackpoint, RecordWorkoutProcessor recordWorkoutProcessor) {
            synchronized (locationList) {
                locationList.add(trackpoint);
                if (locationList.size() >= MIN_STORE_NUM) {
                    WorkoutDatabaseHelper.saveTrackPoints(locationList);
                    locationList.clear();
                    WorkoutDatabaseHelper.save(recordWorkoutProcessor.workout);
                }
            }
        }

        public void flushLocation() {
            Log.v(TAG, "flushLocation, locationList.size() = " + locationList.size());
            synchronized (locationList) {
                if (locationList.size() > 0) {
                    WorkoutDatabaseHelper.saveTrackPoints(locationList);
                    locationList.clear();
                }
            }
        }

        @Override
        public void handleMessage(Message message) {
            RecordWorkoutProcessor recordWorkoutProcessor = reference.get();
            if (recordWorkoutProcessor == null) {
                return;
            }
            int i = message.what;
            if (i == MSG_GPS_GONE) {//1
                recordWorkoutProcessor.warnGpsGoneIfNeed();
            } else if (i == MSG_STORE_LOCATION) {//2
                storeLocation((Trackpoint) message.obj, recordWorkoutProcessor);
            } else if (i == MSG_FLUSH_LOCATION) {//3
                flushLocation();
            }
        }
    }
}